package org.light.domain;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.light.core.Writeable;
import org.light.utils.WriteableUtil;

public class Enum extends Domain implements Comparable<Domain>,Serializable{
	private static final long serialVersionUID = -2427140823588975832L;
	
	public int compareTo(Domain domain) {
		String myName = this.getStandardName();
		String otherName = domain.getStandardName();
		return myName.compareTo(otherName);
	}

	public StatementList generateClassStatementList(){
		String dbRow = "MySqlRow";
		if ("PostgreSQL".equalsIgnoreCase(this.getDbType())||"pgsql".equalsIgnoreCase(this.getDbType())) {
			dbRow = "PgRow";
		} else if ("Oracle".equalsIgnoreCase(this.getDbType())) {
			dbRow = "Row";
		}
		List<Writeable> sList = new ArrayList<Writeable>();
		sList.add(new Statement(500L,0,"#![allow(unused_imports,unused_assignments)]"));
		sList.add(new Statement(1000L,0,"use serde::{Deserialize, Serialize};"));
		if ("MariaDB".equalsIgnoreCase(this.getDbType())||"MySQL".equalsIgnoreCase(this.getDbType())) {
			sList.add(new Statement(2000L,0,"use sqlx::mysql::MySqlRow;"));
			sList.add(new Statement(3000L,0,"use sqlx::{FromRow, Row};"));
			sList.add(new Statement(3010L,0,"use sqlx::Error;"));
		}else if ("PostgreSQL".equalsIgnoreCase(this.getDbType())||"pgsql".equalsIgnoreCase(this.getDbType())) {
			sList.add(new Statement(2000L,0,"use sqlx::postgres::PgRow;"));
			sList.add(new Statement(3000L,0,"use sqlx::{FromRow, Row};"));
			sList.add(new Statement(3010L,0,"use sqlx::Error;"));
		}else if ("Oracle".equalsIgnoreCase(this.getDbType())) {
			sList.add(new Statement(2000L,0,"use oracle::Row;"));
			sList.add(new Statement(3000L,0,"use crate::"+this.getDaoimplSuffix()+"::db_context::FromRow;"));
			sList.add(new Statement(3010L,0,"use oracle::Error;"));
		}
		
		if (containsImage()) {
			sList.add(new Statement(3100L,0,"use crate::"+this.domainSuffix+"::count_num::get_image_base64;"));
		}
		if (containsDateTime()) {
			sList.add(new Statement(3200L,0,"use chrono::{NaiveDate,NaiveDateTime};"));
			sList.add(new Statement(3400L,0,"use crate::"+this.domainSuffix+"::my_date_time_format;"));
			sList.add(new Statement(3600L,0,"use crate::"+this.domainSuffix+"::my_date_format;"));
		}
		sList.add(new Statement(4000L,0,""));
		sList.add(new Statement(5000L,0,"#[derive(Serialize, Deserialize, Clone, Debug)]"));
		sList.add(new Statement(6000L,0,"#[serde(rename_all = \"camelCase\")]"));
		sList.add(new Statement(7000L,0,"pub struct "+this.getCapFirstDomainNameWithSuffix()+" {"));
		sList.add(new Statement(8000L,1,"pub "+this.getDomainId().getSnakeFieldName()+": i64,"));
		sList.add(new Statement(9000L,1,"pub "+this.getDomainName().getSnakeFieldName()+": String,"));
		sList.add(new Statement(10000L,1,"pub "+this.getActive().getSnakeFieldName()+": bool,"));
		long serial = 11000L;
		for (Field f:this.getPlainFields()) {
			if ("Image".equalsIgnoreCase(f.getFieldType())) {
				sList.add(new Statement(serial,1,"pub "+f.getSnakeFieldName()+": String,"));
			}else {
				if (f.getFieldType().equalsIgnoreCase("datetime")) {
					sList.add(new Statement(serial,1,"#[serde(with = \"my_date_time_format\")]"));
				}else if (f.getFieldType().equalsIgnoreCase("date")) {
					sList.add(new Statement(serial,1,"#[serde(with = \"my_date_format\")]"));
				}
				sList.add(new Statement(serial+500L,1,"pub "+f.getSnakeFieldName()+": "+f.getClassType()+","));
			}
			serial += 1000L;			
		}
		sList.add(new Statement(serial,0,"}"));
		sList.add(new Statement(serial+1000L,0,""));
		
		sList.add(new Statement(serial+5000L,0,"#[derive(Serialize, Deserialize, Clone)]"));
		sList.add(new Statement(serial+6000L,0,"#[serde(rename_all = \"camelCase\")]"));
		sList.add(new Statement(serial+7000L,0,"pub struct "+this.getCapFirstDomainName()+"QueryRequest {"));
		sList.add(new Statement(serial+8000L,1,"pub "+this.getDomainId().getSnakeFieldName()+": Option<String>,"));
		sList.add(new Statement(serial+9000L,1,"pub "+this.getDomainName().getSnakeFieldName()+": Option<String>,"));
		sList.add(new Statement(serial+10000L,1,"pub "+this.getActive().getSnakeFieldName()+": Option<String>,"));
		serial += 11000L;
		for (Field f:this.getPlainFields()) {
			sList.add(new Statement(serial,1,"pub "+f.getSnakeFieldName()+": Option<String>,"));
			serial += 1000L;
		}
		sList.add(new Statement(serial,1,"pub page:Option<i32>,"));
		sList.add(new Statement(serial+1000L,1,"pub rows:Option<i32>,"));
		sList.add(new Statement(serial+1200L,"}"));
		sList.add(new Statement(serial+1400L,0,""));
		
		if ("Oracle".equalsIgnoreCase(this.getDbType())) {
			sList.add(new Statement(serial+2000L,0,"impl<'c> FromRow<'c, "+dbRow+"> for "+this.getCapFirstDomainNameWithSuffix()+" {"));
			sList.add(new Statement(serial+3000L,1,"fn from_row(row: &"+dbRow+") -> Result<Self, Error> {"));
			
			sList.add(new Statement(serial+4000L,2,"let "+this.active.getSnakeFieldName()+"_int:i32 = row.get(2).unwrap();"));

			serial += 5000L;
			int pos0 = 3;
			for (Field f:this.getPlainFields()) {
				if ("bool".equalsIgnoreCase(f.getFieldType())) {
					sList.add(new Statement(serial,2,"let "+f.getSnakeFieldName()+"_int:i32 = row.get("+pos0+").unwrap();"));
					serial += 1000L;
				}else if ("Image".equalsIgnoreCase(f.getFieldType())) {
					sList.add(new Statement(serial+1000L,2,"let "+f.getSnakeFieldName()+" = row.get("+pos0+");"));
					sList.add(new Statement(serial+2000L,2,"let mut "+f.getSnakeFieldName()+"_str = \"\".to_string();"));
					sList.add(new Statement(serial+3000L,2,"match "+f.getSnakeFieldName()+" {"));
					sList.add(new Statement(serial+4000L,3,"Ok(bytes) => "+f.getSnakeFieldName()+"_str = get_image_base64(bytes),"));
					sList.add(new Statement(serial+5000L,3,"Err(_) => "+f.getSnakeFieldName()+"_str = \"\".to_string(),"));
					sList.add(new Statement(serial+6000L,2,"}"));
				}
				serial += 10000L;
				pos0++;
			}
			
			sList.add(new Statement(serial+4000L,2,"Ok("+this.getCapFirstDomainNameWithSuffix()+" {"));
			sList.add(new Statement(serial+5000L,3,""+this.getDomainId().getSnakeFieldName()+": row.get(0).unwrap(),"));
			sList.add(new Statement(serial+6000L,3,""+this.getDomainName().getSnakeFieldName()+": row.get(1).unwrap(),"));
			sList.add(new Statement(serial+7000L,3,this.getActive().getSnakeFieldName()+": "+this.getActive().getSnakeFieldName()+"_int == 1 ,"));
			serial += 8000L;
			int pos = 3;
			for (Field f:this.getPlainFields()) {
				if ("Image".equalsIgnoreCase(f.getFieldType())) {
					sList.add(new Statement(serial,3,f.getSnakeFieldName()+":"+f.getSnakeFieldName()+"_str,"));
				} else if ("bool".equalsIgnoreCase(f.getFieldType())) {
					sList.add(new Statement(serial,3,f.getSnakeFieldName()+":"+f.getSnakeFieldName()+"_int == 1,"));
				} else {
					sList.add(new Statement(serial,3,f.getSnakeFieldName()+":row.get("+pos+").unwrap_or_default(),"));
				}
				serial += 1000L;
				pos++;
			}
			sList.add(new Statement(serial,2,"})"));
			sList.add(new Statement(serial+1000L,1,"}"));
			sList.add(new Statement(serial+2000L,0,"}"));
			serial += 3000L;
		}else {		
			sList.add(new Statement(serial+2000L,0,"impl<'c> FromRow<'c, "+dbRow+"> for "+this.getCapFirstDomainNameWithSuffix()+" {"));
			sList.add(new Statement(serial+3000L,1,"fn from_row(row: &"+dbRow+") -> Result<Self, Error> {"));
			sList.add(new Statement(serial+4000L,2,"Ok("+this.getCapFirstDomainNameWithSuffix()+" {"));
			sList.add(new Statement(serial+5000L,3,""+this.getDomainId().getSnakeFieldName()+": row.get(0),"));
			sList.add(new Statement(serial+6000L,3,""+this.getDomainName().getSnakeFieldName()+": row.get(1),"));
			sList.add(new Statement(serial+7000L,3,this.getActive().getSnakeFieldName()+": row.get(2),"));
			serial += 8000L;
			int pos = 3;
			for (Field f:this.getPlainFields()) {
				if ("Image".equalsIgnoreCase(f.getFieldType())) {
					sList.add(new Statement(serial,3,f.getSnakeFieldName()+":get_image_base64(row.get("+pos+")),"));
				}else {
					sList.add(new Statement(serial,3,f.getSnakeFieldName()+":row.get("+pos+"),"));
				}
				serial += 1000L;
				pos++;
			}
			sList.add(new Statement(serial,2,"})"));
			sList.add(new Statement(serial+1000L,1,"}"));
			sList.add(new Statement(serial+2000L,0,"}"));
			serial += 3000L;
		}
		sList.add(new Statement(serial+2200L,0,""));
		sList.add(new Statement(serial+3000L,0,"impl PartialEq for "+this.getCapFirstDomainNameWithSuffix()+" {"));
		sList.add(new Statement(serial+4000L,1,"fn eq(&self, other: &Self) -> bool {"));
		sList.add(new Statement(serial+5000L,2,"self."+this.getDomainId().getSnakeFieldName()+" == other."+this.getDomainId().getSnakeFieldName()));
		sList.add(new Statement(serial+6000L,1,"}"));
		sList.add(new Statement(serial+7000L,0,"}"));


		return WriteableUtil.merge(sList);
	}
	
	public Domain toDomain() throws Exception{
		Domain domain = new Domain();
		domain.setStandardName(this.getStandardName());
		domain.setPlural(this.getPlural());
		domain.setTablePrefix(this.getTablePrefix());
		domain.setLabel(this.getLabel());
		domain.setVerbDeniesStr(this.getVerbDeniesStr());
		
		domain.setSerial(this.getSerial());
		domain.setDbPrefix(this.getPureDbPrefix());
		domain.setPackageToken(this.getPackageToken());
		domain.setLanguage(this.getLanguage());
		domain.setSchema(this.getSchema());
		domain.setDomainSuffix(this.getDomainSuffix());
		domain.setDomainNamingSuffix(this.getDomainNamingSuffix());
		domain.setDaoSuffix(this.getDaoSuffix());
		domain.setDaoimplSuffix(this.getDaoimplSuffix());
		domain.setServiceSuffix(this.getServiceSuffix());
		domain.setServiceimplSuffix(this.getServiceimplSuffix());
		domain.setControllerSuffix(this.getControllerSuffix());
		domain.setControllerNamingSuffix(this.getControllerNamingSuffix());
		domain.setProjectName(this.getStandardName());
		domain.setDbType(this.getDbType());
		domain.setControllerPackage(this.getControllerSuffix());
		domain.setFrontendUi(this.getFrontendUi());
		domain.setBackendUi(this.getBackendUi());
		
		if (this.getDomainId() != null) domain.setDomainId(this.getDomainId());
		if (this.getDomainName() != null) domain.setDomainName(this.getDomainName());
		if (this.getActive() != null) domain.setActive(this.getActive());
		domain.setFields(this.getPlainFields());
		domain.setManyToManies(this.getManyToManies());
		
		List<Domain> dummyDomains = new ArrayList<>();
		for (Domain d:this.getDummyDb()) {
			Domain domain0 = (Domain) domain.deepClone();
			if (d.getDomainId() != null) domain0.setDomainId(d.getDomainId());
			if (d.getDomainName() != null) domain0.setDomainName(d.getDomainName());
			if (d.getActive() != null) domain0.setActive(d.getActive());
			domain0.setFields(d.getPlainFields());
			domain0.setManyToManies(d.getManyToManies());
			dummyDomains.add(domain0);
		}
		domain.setDummyDb(dummyDomains);
		
		return domain;
	}
}
